package nio.exercise;

import sun.misc.Cleaner;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @Desc:   测试 stream 、 channel 、 mapped 三种方式对文件进行写，发现新建文件写，其实没太大差别，
 *          因为IO性能最终受OS和 CPU 影响，而在写小文件的时候基于流更快，因为省去了通道打开关闭等操作
 *          但是，如果是文件的拷贝，那么基于通道和映射就能够发挥其作用了
 *          如果是对一个文件内容的频繁读取，那么内存隐射就能够发挥其作用了
 *          https://www.cnblogs.com/gaopeng527/p/4896783.html
 *
 *          1MB --> SSD
 *          writeByStream cause: 2
 *          writeByChannel cause: 4
 *          writeByMapped cause: 35
 *
 *          1GB --> SSD
 *          writeByStream cause: 4219
 *          writeByChannel cause: 3487
 *
 *          1GB --> HDD
 *          writeByStream cause: 7737
 *          writeByChannel cause: 7318
 *
 *          1024*1024*1124 -->HDD
 *          writeByStream cause: 8418
 *          writeByChannel cause: 8419
 *
 *          1.95 GB -->HDD
 *          writeByStream cause: 14065
 *          writeByChannel cause: 14401
 *          writeByMapped cause: 14799
 * @author: cww
 * @DateTime: 2020-03-30 22:34
 */

public class ChannelVSStream {

    private static byte[] buf = new byte[1024*1024];

    public static void main(String[] args) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        stuffBuf();
//        writeByStream();
//        writeByChannel();
        writeByMapped();
    }

    /**
     * 将 buf 填满
     */
    public static void stuffBuf() {
        for (int i = 0; i < buf.length; i++) {
            buf[i] = 127;
        }

    }

    /**
     * 通过流写文件
     */
    public static void writeByStream() throws IOException {
        long start = System.currentTimeMillis();
        FileOutputStream out = new FileOutputStream("G:\\split\\stream.txt");
        out.write(buf);
        out.flush();
        out.close();
        long end = System.currentTimeMillis();
        System.out.println("writeByStream cause: " + (end-start));
    }

    /**
     * 通过通道写文件
     */
    public static void writeByChannel() throws FileNotFoundException {
        long start = System.currentTimeMillis();
        try {
            RandomAccessFile file = new RandomAccessFile(new File("G:\\split\\channel.txt"),"rw");
            FileChannel channel = file.getChannel();
            ByteBuffer buffer = ByteBuffer.wrap(buf);
            if (buffer.hasRemaining()) {
                channel.write(buffer);
            }
            file.close();
            channel.close();
            buffer.clear();
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("writeByChannel cause: " + (end-start));
    }

    /**
     * 通过内存映射
     * @throws FileNotFoundException
     */
    public static void writeByMapped() throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        long start = System.currentTimeMillis();
        RandomAccessFile file = new RandomAccessFile(new File("G:\\split\\mapped.txt"),"rw");
        FileChannel channel = file.getChannel();
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE,0,buf.length);
        mappedByteBuffer.put(buf);
        mappedByteBuffer.force();
        long end = System.currentTimeMillis();
        System.out.println("writeByMapped cause: " + (end-start));
        Method method = mappedByteBuffer.getClass().getMethod("cleaner",new Class[0]);
        method.setAccessible(true);
        Cleaner cleaner = (Cleaner) method.invoke(mappedByteBuffer, new Class[0]);
        cleaner.clean();
    }
}
